package org.example.projecttwo.service.Impl.user;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.projecttwo.entity.College;
import org.example.projecttwo.entity.dto.auth.LoginDTO;
import org.example.projecttwo.entity.dto.auth.LoginVO;
import org.example.projecttwo.entity.dto.auth.RegisterDTO;
import org.example.projecttwo.entity.dto.common.BatchDeleteUserDTO;
import org.example.projecttwo.entity.dto.common.BatchUpdateUserStatusDTO;
import org.example.projecttwo.entity.dto.common.DeleteUserDTO;
import org.example.projecttwo.entity.dto.user.request.ChangePasswordDTO;
import org.example.projecttwo.entity.dto.user.request.UpdatePersonalityDTO;
import org.example.projecttwo.entity.dto.user.request.UpdateProfileDTO;
import org.example.projecttwo.entity.dto.user.response.UserExportDTO;
import org.example.projecttwo.entity.user.User;
import org.example.projecttwo.entity.user.UserDeleted;
import org.example.projecttwo.entity.user.UserStatistics;
import org.example.projecttwo.mapper.user.UserMapper;
import org.example.projecttwo.service.BaseService;
import org.example.projecttwo.service.Icommon.IRedisService;
import org.example.projecttwo.service.Impl.CollegeServiceImpl;
import org.example.projecttwo.service.Iuser.IUserDeletedService;
import org.example.projecttwo.service.Iuser.IUserService;
import org.example.projecttwo.service.Iuser.IUserStatisticsService;
import org.example.projecttwo.entity.enums.PointActionEnum;
import org.example.projecttwo.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户基础信息表 服务实现类
 * </p>
 *
 * @author nn
 * @since 2025-11-01
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private IUserStatisticsService userStatisticsService;

    @Autowired
    private BaseService baseService;

    @Autowired
    private IRedisService redisService;

    @Autowired
    private IUserDeletedService userDeletedService;

    @Autowired
    private CollegeServiceImpl collegeService;


    @Override
    @Transactional
    public void register(RegisterDTO dto) {
        // 1. 校验用户名(账号)唯一性
        if (isUsernameExists(dto.getUsername())) {
            throw new RuntimeException("用户名已存在");
        }

        // 2. 校验密码一致性
        if (!dto.getPassword().equals(dto.getConfirmPassword())) {
            throw new RuntimeException("密码与确认密码不一致");
        }

        // 3. 创建用户
        User user = new User();
        user.setUserNo(generateUserNo()); // 生成用户编号
        user.setUsername(dto.getUsername());
        user.setNickname(dto.getNickname()); // 设置昵称
        user.setPassword(passwordEncoder.encode(dto.getPassword())); // 加密
        user.setStatus(0); // 启用
        user.setRoleCode("USER");// 普通用户
        user.setPoints(0); // 初始化积分为0

        // 生成用户编号，最多尝试3次
        boolean userNoGenerated = false;
        for (int i = 0; i < 3; i++) {
            try {
                user.setUserNo(generateUserNo());
                this.save(user);
                userNoGenerated = true;
                break;
            } catch (Exception e) {
                // 如果是唯一性约束违反异常，则重试
                if (e.getMessage().contains("uk_user_no") || e.getMessage().contains("Duplicate")) {
                    // 等待一小段时间后重试
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("注册过程中断");
                    }
                    continue;
                } else {
                    throw e; // 其他异常直接抛出
                }
            }
        }

        if (!userNoGenerated) {
            throw new RuntimeException("用户编号生成失败，请稍后重试");
        }

        // 4. 初始化统计数据
        UserStatistics stats = new UserStatistics();
        stats.setUserId(user.getId());
        stats.setArticleCount(0);
        stats.setNewsCount(0);
        stats.setLikeCount(0);
        stats.setCollectCount(0);
        userStatisticsService.save(stats);
    }

    @Override
    public LoginVO login(LoginDTO dto) {
        User user = this.lambdaQuery()
                .eq(User::getUsername, dto.getUsername())
                .one();

        // 检查账号是否被锁定
        if (user != null && user.getLockTime() != null && user.getLockTime().isAfter(LocalDateTime.now())) {
            throw new RuntimeException("账号已被锁定，请1小时后再试");
        }

        if (user == null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
            // 登录失败，增加失败次数
            if (user != null) {
                handleLoginFailure(user);
            }
            throw new RuntimeException("用户名或密码错误");
        }

        if (user.getStatus() != 0) {
            throw new RuntimeException("账号已被禁用");
        }

        // 登录成功，重置失败次数
        resetLoginFailCount(user);

        // 生成 JWT Token-包含用户ID(userId)
        String token = jwtUtil.generateToken(user.getId());

        return new LoginVO(token, user);
    }

    /**
     * 处理登录失败，增加失败次数，超过4次则锁定账号
     * @param user 用户对象
     */
    private void handleLoginFailure(User user) {
        Integer failCount = user.getLoginFailCount();
        if (failCount == null) {
            failCount = 0;
        }
        
        failCount++;
        
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setLoginFailCount(failCount);
        
        // 如果失败次数超过4次，锁定账号1小时
        if (failCount > 4) {
            updateUser.setLockTime(LocalDateTime.now().plusHours(1));
        }
        
        this.updateById(updateUser);
    }

    /**
     * 重置登录失败次数
     * @param user 用户对象
     */
    private void resetLoginFailCount(User user) {
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setLoginFailCount(0);
        updateUser.setLockTime(null);
        this.updateById(updateUser);
    }

    @Override
    public IPage<User> pageBy(Page<User> page, String keyword, Integer status, Integer authType, Integer role, String collegeId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();

        //根据昵称查询
        if (keyword != null && !keyword.isEmpty()) {
            queryWrapper.like(User::getNickname, keyword);
        }

        //账号状态筛选
        if (status != null) {
            queryWrapper.eq(User::getStatus, status);
        }

        //认证类型筛选
        if (authType != null) {
            queryWrapper.eq(User::getAuthType, authType);
        }

        //角色筛选
        if (role != null) {
            String roleCode = role == 0 ? "ADMIN" : "USER";
            queryWrapper.eq(User::getRoleCode, roleCode);
        }

        //学院筛选
        if (collegeId != null && !collegeId.isEmpty()) {
            queryWrapper.eq(User::getCollegeId, Long.valueOf(collegeId));
        }

        return this.page(page, queryWrapper);
    }

    @Override
    public User getCurrentUser() {
        Long currentUserId = baseService.getCurrentUserId();
        return this.getById(currentUserId);
    }

    @Override
    public void updateCurrentUser(UpdatePersonalityDTO userDto) {
        Long currentUserId = baseService.getCurrentUserId();

        // 检查是否需要更新昵称
        User currentUser = this.getById(currentUserId);
        if (userDto.getNickname() != null && !userDto.getNickname().equals(currentUser.getNickname())) {
            // 验证昵称更新频率（每小时只能更新一次）
            checkNicknameUpdateFrequency(currentUserId);
        }

        // 检查个性标签限制
        if (userDto.getTagsJson() != null) {
            validateTags(userDto.getTagsJson());
        }

        User updateEntity = new User();
        updateEntity.setId(currentUserId);
        updateEntity.setNickname(userDto.getNickname());
        updateEntity.setAvatar(userDto.getAvatar());
        updateEntity.setSignature(userDto.getSignature());
        updateEntity.setTagsJson(userDto.getTagsJson());
        updateEntity.setUpdateTime(LocalDateTime.now());
        this.updateById(updateEntity);

        // 如果昵称有更新，记录更新时间到Redis
        if (userDto.getNickname() != null && !userDto.getNickname().equals(currentUser.getNickname())) {
            recordNicknameUpdateTime(currentUserId);
        }
    }

    @Override
    @Transactional
    public void updateProfile(UpdateProfileDTO dto) {
        Long currentUserId = baseService.getCurrentUserId();
        User user = new User();
        user.setId(currentUserId);
        user.setPhone(dto.getPhone());
        user.setEmail(dto.getEmail());
        user.setCollegeId(dto.getCollegeId());

        // 根据 collegeId 查询学院名称（保持数据一致性）
        if (dto.getCollegeId() != null) {
            College college = collegeService.getById(dto.getCollegeId());
            user.setCollegeName(college != null ? college.getName() : null);
        }

        user.setMajorClass(dto.getMajorClass());
        user.setStudentNo(dto.getStudentNo());
        user.setGender(dto.getGender());
        user.setBirthday(dto.getBirthday());
        user.setUpdateTime(LocalDateTime.now());
        // registerTime是只读字段，不进行更新

        this.updateById(user);
    }

    @Override
    @Transactional
    public void changePassword(ChangePasswordDTO dto) {
        if (!dto.getNewPassword().equals(dto.getConfirmPassword())) {
            throw new RuntimeException("新密码与确认密码不一致");
        }

        Long currentUserId = baseService.getCurrentUserId();
        User currentUser = this.getById(currentUserId);
        if (currentUser == null) {
            throw new RuntimeException("当前用户不存在");
        }

        // 验证旧密码
        if (!passwordEncoder.matches(dto.getOldPassword(), currentUser.getPassword())) {
            throw new RuntimeException("旧密码错误");
        }

        // 更新密码
        User user = new User();
        user.setId(currentUserId);
        user.setPassword(passwordEncoder.encode(dto.getNewPassword()));
        user.setPasswordUpdateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        this.updateById(user);
    }

    @Override
    @Transactional
    public void updateUserStatus(Long id, Integer status) {
        User user = new User();
        user.setId(id);
        user.setStatus(status);
        this.updateById(user);
    }

    @Override
    @Transactional
    public void resetUserPassword(Long id) {
        User user = new User();
        user.setId(id);
        user.setPassword(passwordEncoder.encode("123456"));
        user.setPasswordUpdateTime(LocalDateTime.now());
        this.updateById(user);
    }

    @Override
    @Transactional
    public void deleteUser(Long id, DeleteUserDTO deleteDto) {
        // 获取要删除的用户信息
        User user = this.getById(id);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 创建用户删除记录
        UserDeleted userDeleted = new UserDeleted();
        userDeleted.setUserId(user.getId());
        userDeleted.setUsername(user.getUsername());
        userDeleted.setDeleteReason(deleteDto.getDeleteReason());
        userDeleted.setDeleteRemark(deleteDto.getDeleteRemark());
        userDeleted.setDeletedTime(LocalDateTime.now());
        userDeleted.setRestoreDeadline(LocalDateTime.now().plusDays(10)); // 10天后过期
        userDeleted.setDeletedBy(baseService.getCurrentUserId());
        userDeleted.setOperatorNickname(this.getById(baseService.getCurrentUserId()).getNickname());
        
        // 保存删除记录
        userDeletedService.save(userDeleted);
        
        // 删除用户
        this.removeById(id);
    }
    
    @Override
    @Transactional
    public User createUser(User user) {
        // 检查用户名唯一性
        if (this.lambdaQuery().eq(User::getUsername, user.getUsername()).exists()) {
            throw new RuntimeException("用户名已存在");
        }

        // 设置默认值
        user.setStatus(user.getStatus() == null ? 0 : user.getStatus()); // 默认启用
        user.setRoleCode(user.getRoleCode() == null ? "USER" : user.getRoleCode()); // 默认普通用户
        user.setPoints(user.getPoints() == null ? 0 : user.getPoints()); // 默认积分0
        user.setPassword(passwordEncoder.encode(user.getPassword())); // 加密密码
        
        // 生成用户编号
        user.setUserNo(generateUserNo());
        
        // 保存用户
        this.save(user);
        
        // 初始化统计数据
        UserStatistics stats = new UserStatistics();
        stats.setUserId(user.getId());
        stats.setArticleCount(0);
        stats.setNewsCount(0);
        stats.setLikeCount(0);
        stats.setCollectCount(0);
        stats.setContinuousCheckinDays(0);
        stats.setTotalCheckinDays(0);
        userStatisticsService.save(stats);
        
        return user;
    }
    
    @Override
    @Transactional
    public User updateUser(User user) {
        // 检查用户是否存在
        User existingUser = this.getById(user.getId());
        if (existingUser == null) {
            throw new RuntimeException("用户不存在");
        }
        
        // 检查昵称更新频率（每小时只能更新一次）
        if (user.getNickname() != null && !user.getNickname().equals(existingUser.getNickname())) {
            checkNicknameUpdateFrequency(user.getId());
        }
        
        // 检查个性标签限制
        if (user.getTagsJson() != null) {
            validateTags(user.getTagsJson());
        }
        
        // 更新时间
        user.setUpdateTime(LocalDateTime.now());
        
        // 更新用户信息
        this.updateById(user);
        
        // 如果昵称有更新，记录更新时间到Redis
        if (user.getNickname() != null && !user.getNickname().equals(existingUser.getNickname())) {
            recordNicknameUpdateTime(user.getId());
        }
        
        return this.getById(user.getId());
    }

    /**
     * 检查昵称更新频率（每小时只能更新一次）
     *
     * @param userId 用户ID
     */
    private void checkNicknameUpdateFrequency(Long userId) {
        String key = "user:nickname:last_update:" + userId;
        if (redisService.hasKey(key)) {
            throw new RuntimeException("昵称每小时只能更改一次");
        }
    }

    /**
     * 记录昵称更新时间
     *
     * @param userId 用户ID
     */
    private void recordNicknameUpdateTime(Long userId) {
        String key = "user:nickname:last_update:" + userId;
        redisService.set(key, System.currentTimeMillis(), 1, TimeUnit.HOURS);
    }

    /**
     * 验证个性标签限制
     * 1. 不得超过8个标签
     * 2. 每个标签不超过5个字
     *
     * @param tagsJson 标签JSON字符串
     */
    private void validateTags(String tagsJson) {
        if (tagsJson == null || tagsJson.trim().isEmpty()) {
            return;
        }

        try {
            // 使用Fastjson解析JSON数组
            com.alibaba.fastjson.JSONArray tagsArray = com.alibaba.fastjson.JSON.parseArray(tagsJson);

            // 检查标签数量
            if (tagsArray.size() > 8) {
                throw new RuntimeException("个性标签不得超过8个");
            }

            // 检查每个标签的长度
            for (Object tagObj : tagsArray) {
                if (tagObj instanceof String tag) {
                    if (tag.length() > 5) {
                        throw new RuntimeException("每个标签不得超过5个字");
                    }
                } else {
                    throw new RuntimeException("标签格式错误");
                }
            }
        } catch (com.alibaba.fastjson.JSONException e) {
            throw new RuntimeException("标签格式错误");
        }
    }

    /**
     * 生成用户编号
     * 格式：年份+01+注册序号（例如：2025011）
     *
     * @return 用户编号
     */
    private String generateUserNo() {
        // 获取当前年份
        String year = String.valueOf(java.time.Year.now().getValue());

        // 使用雪花算法ID的后几位作为序号，确保唯一性
        long userCount = System.currentTimeMillis() % 1000000;

        // 拼接用户编号：年份+01+序号
        return year + "01" + userCount;
    }

    /**
     * 检查用户名是否已存在（用于新增用户校验）
     * @param username 待检查的用户名
     * @return true: 已存在; false: 不存在
     */
    private boolean isUsernameExists(String username) {
        return this.lambdaQuery()
                .eq(User::getUsername, username)
                .exists();
    }

    /**
     * 转换角色编码
     * @param roleCode 0:普通用户 1:管理员 2:超级管理员
     * @return 角色编码字符串
     */
    private String convertRoleCode(Integer roleCode) {
        if (roleCode == null) {
            return "USER"; // 默认普通用户
        }
        return switch (roleCode) {
            case 0 -> "USER";      // 普通用户
            case 1 -> "ADMIN";     // 管理员
            case 2 -> "SUPER_ADMIN"; // 超级管理员
            default -> "USER";     // 默认普通用户
        };
    }

    @Override
    @Transactional
    public void batchUpdateUserStatus(BatchUpdateUserStatusDTO dto) {
        List<Long> ids = dto.getIds();
        Integer status = dto.getStatus();
        
        // 批量更新用户状态
        this.lambdaUpdate()
                .in(User::getId, ids)
                .set(User::getStatus, status)
                .update();
    }

    @Override
    @Transactional
    public void batchDeleteUser(BatchDeleteUserDTO dto) {
        List<Long> ids = dto.getIds();
        Integer deleteReason = dto.getDeleteReason();
        String deleteRemark = dto.getDeleteRemark();
        Long currentUserId = baseService.getCurrentUserId();
        String operatorNickname = this.getById(currentUserId).getNickname();
        
        // 查询要删除的用户
        List<User> users = this.lambdaQuery()
                .in(User::getId, ids)
                .list();
        
        // 创建删除记录
        List<UserDeleted> userDeletedList = users.stream().map(user -> {
            UserDeleted userDeleted = new UserDeleted();
            userDeleted.setUserId(user.getId());
            userDeleted.setUsername(user.getUsername());
            userDeleted.setDeleteReason(deleteReason);
            userDeleted.setDeleteRemark(deleteRemark);
            userDeleted.setDeletedTime(LocalDateTime.now());
            userDeleted.setRestoreDeadline(LocalDateTime.now().plusDays(10)); // 10天后过期
            userDeleted.setDeletedBy(currentUserId);
            userDeleted.setOperatorNickname(operatorNickname);
            return userDeleted;
        }).toList();
        
        // 批量保存删除记录
        userDeletedService.saveBatch(userDeletedList);
        
        // 批量删除用户
        this.removeBatchByIds(ids);
    }

    @Override
    public List<UserExportDTO> batchExportUser(List<Long> ids) {
        List<User> users = this.lambdaQuery()
                .in(User::getId, ids)
                .list();
        
        return users.stream().map(user -> {
            UserExportDTO userExportDTO = new UserExportDTO();
            userExportDTO.setId(user.getId());
            userExportDTO.setUserNo(user.getUserNo());
            userExportDTO.setUsername(user.getUsername());
            userExportDTO.setNickname(user.getNickname());
            userExportDTO.setAvatar(user.getAvatar());
            userExportDTO.setPhone(user.getPhone());
            userExportDTO.setEmail(user.getEmail());
            userExportDTO.setCollegeId(user.getCollegeId());
            userExportDTO.setCollegeName(user.getCollegeName());
            userExportDTO.setStatus(user.getStatus());
            userExportDTO.setRoleCode(user.getRoleCode());
            userExportDTO.setAuthType(user.getAuthType());
            userExportDTO.setPoints(user.getPoints());
            userExportDTO.setRegisterTime(user.getRegisterTime());
            userExportDTO.setLastLoginTime(user.getLastLoginTime());
            userExportDTO.setSignature(user.getSignature());
            userExportDTO.setGender(user.getGender());
            userExportDTO.setBirthday(user.getBirthday());
            userExportDTO.setMajorClass(user.getMajorClass());
            userExportDTO.setStudentNo(user.getStudentNo());
            userExportDTO.setTagsJson(user.getTagsJson());
            userExportDTO.setLastNicknameUpdateTime(user.getLastNicknameUpdateTime());
            userExportDTO.setUpdateTime(user.getUpdateTime());
            userExportDTO.setLoginFailCount(user.getLoginFailCount());
            userExportDTO.setLockTime(user.getLockTime());
            userExportDTO.setPasswordUpdateTime(user.getPasswordUpdateTime());
            return userExportDTO;
        }).toList();
    }

    @Override
    public UserExportDTO exportCurrentUser() {
        Long currentUserId = baseService.getCurrentUserId();
        User user = this.getById(currentUserId);
        
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        
        UserExportDTO userExportDTO = new UserExportDTO();
        userExportDTO.setId(user.getId());
        userExportDTO.setUserNo(user.getUserNo());
        userExportDTO.setUsername(user.getUsername());
        userExportDTO.setNickname(user.getNickname());
        userExportDTO.setAvatar(user.getAvatar());
        userExportDTO.setPhone(user.getPhone());
        userExportDTO.setEmail(user.getEmail());
        userExportDTO.setCollegeId(user.getCollegeId());
        userExportDTO.setCollegeName(user.getCollegeName());
        userExportDTO.setStatus(user.getStatus());
        userExportDTO.setRoleCode(user.getRoleCode());
        userExportDTO.setAuthType(user.getAuthType());
        userExportDTO.setPoints(user.getPoints());
        userExportDTO.setRegisterTime(user.getRegisterTime());
        userExportDTO.setLastLoginTime(user.getLastLoginTime());
        userExportDTO.setSignature(user.getSignature());
        userExportDTO.setGender(user.getGender());
        userExportDTO.setBirthday(user.getBirthday());
        userExportDTO.setMajorClass(user.getMajorClass());
        userExportDTO.setStudentNo(user.getStudentNo());
        userExportDTO.setTagsJson(user.getTagsJson());
        userExportDTO.setLastNicknameUpdateTime(user.getLastNicknameUpdateTime());
        userExportDTO.setUpdateTime(user.getUpdateTime());
        userExportDTO.setLoginFailCount(user.getLoginFailCount());
        userExportDTO.setLockTime(user.getLockTime());
        userExportDTO.setPasswordUpdateTime(user.getPasswordUpdateTime());
        
        return userExportDTO;
    }

    @Override
    @Transactional
    public void switchUserStatus(Long id, Integer status) {
        User user = new User();
        user.setId(id);
        user.setStatus(status);
        this.updateById(user);
    }

    @Override
    @Transactional
    public void switchUserRole(Long id, Integer roleCode) {
        User user = new User();
        user.setId(id);
        user.setRoleCode(convertRoleCode(roleCode));
        this.updateById(user);
    }

    @Override
    @Transactional
    public void adjustUserPoints(Long id, Integer type, Integer value) {
        // 查询用户当前积分
        User currentUser = this.getById(id);
        if (currentUser == null) {
            throw new RuntimeException("用户不存在");
        }
        
        // 计算调整后的积分
        int currentPoints = currentUser.getPoints() != null ? currentUser.getPoints() : 0;
        int newPoints = type == 1 ? currentPoints + value : currentPoints - value;
        
        // 确保积分不为负数
        if (newPoints < 0) {
            newPoints = 0;
        }
        
        // 更新用户积分
        User user = new User();
        user.setId(id);
        user.setPoints(newPoints);
        this.updateById(user);
    }

    @Override
    @Transactional
    public void adjustUserPointsByAction(Long userId, PointActionEnum action) {
        // 查询用户当前积分
        User currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new RuntimeException("用户不存在");
        }
        
        // 计算调整后的积分
        int currentPoints = currentUser.getPoints() != null ? currentUser.getPoints() : 0;
        int newPoints = currentPoints + action.getPoints();
        
        // 确保积分不为负数
        if (newPoints < 0) {
            newPoints = 0;
        }
        
        // 更新用户积分
        User user = new User();
        user.setId(userId);
        user.setPoints(newPoints);
        this.updateById(user);
    }
}